package cn.tedu.mall.seckill.service.impl;

import cn.tedu.mall.common.exception.CoolSharkServiceException;
import cn.tedu.mall.common.pojo.domain.CsmallAuthenticationInfo;
import cn.tedu.mall.common.restful.ResponseCode;
import cn.tedu.mall.order.service.IOmsOrderService;
import cn.tedu.mall.pojo.order.dto.OrderAddDTO;
import cn.tedu.mall.pojo.order.dto.OrderItemAddDTO;
import cn.tedu.mall.pojo.order.vo.OrderAddVO;
import cn.tedu.mall.pojo.seckill.dto.SeckillOrderAddDTO;
import cn.tedu.mall.pojo.seckill.model.Success;
import cn.tedu.mall.pojo.seckill.vo.SeckillCommitVO;
import cn.tedu.mall.seckill.config.RabbitMqComponentConfiguration;
import cn.tedu.mall.seckill.service.ISeckillService;
import cn.tedu.mall.seckill.utils.SeckillCacheUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
@Slf4j
public class SeckillServiceImpl implements ISeckillService {
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @DubboReference
    private IOmsOrderService dubboOrderService;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    /*
      1. 判断当前用户是否为重复购买
      2. 从Redis中判断是否有库存
      3. 秒杀订单转成普通订单，需要使用Dubbo调用order模块完成
      4. 秒杀成功的消息并不需要立即处理，使用消息队列的方式将秒杀成功的信息保存在success表中
     */
    @Override
    public SeckillCommitVO commitSeckill(SeckillOrderAddDTO seckillOrderAddDTO) {
        //第一部分：利用Redis去检查是否重复购物和库存是否充足
        //先获取userId和skuId
        Long userId = getUserId();
        Long skuId = seckillOrderAddDTO.getSeckillOrderItemAddDTO().getSkuId();
        //利用userId和skuId组合去判断是否为重复购买
        //将userId和skuId的组合成一个key，存入redis中，利用Redis判断是否为重复购买
        String reseckillCheckKey = SeckillCacheUtils.getReseckillCheckKey(skuId, userId);
        //利用increment()，作用是如果没有这个key，会保存该key，并设置value为1
        //如果有这个key，在原有的value默认+1，并把值返回给调用者
        Long increment = stringRedisTemplate.boundValueOps(reseckillCheckKey).increment();
        //判断increment，如果大于1就是重复购买
        if(increment>1)
            throw new CoolSharkServiceException(ResponseCode.FORBIDDEN,"您已经购买过该商品了！");
        //程序到这里，表示是第一个购买
        //判断库存是否充足，利用decrement(),调用一次默认-1,当前用户发起购买请求，只有库存不小于0就是库存充足
        //先获取skuId的key
        String stockKey = SeckillCacheUtils.getStockKey(skuId);
        Long decrement = stringRedisTemplate.boundValueOps(stockKey).decrement();
        if(decrement<0){
            //删除购买记录
            stringRedisTemplate.boundValueOps(reseckillCheckKey).decrement();
            //提示库存不足
            throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST,"对不起，您购买商品已售罄");
        }
        //第二部分：将秒杀订单转成普通订单
        OrderAddDTO orderAddDTO = convertSeckillOrderToOrder(seckillOrderAddDTO);
        //设置userId
        orderAddDTO.setUserId(userId);
        //普通订单信息准备好了，生成普通订单
        OrderAddVO orderAddVO = dubboOrderService.addOrder(orderAddDTO);
        //第三部分：使用消息队列保存秒杀成功记录的信息
        Success success = new Success();
        success.setUserId(userId); //用户id
        success.setOrderSn(orderAddVO.getSn());//订单编号
        success.setQuantity(seckillOrderAddDTO.getSeckillOrderItemAddDTO().getQuantity());//成功的购买的商品数据
        //发送到RabbitMQ中
        rabbitTemplate.convertAndSend(RabbitMqComponentConfiguration.SECKILL_EX,
                RabbitMqComponentConfiguration.SECKILL_RK,success);
        //声明当前方法返回值类型对象
        SeckillCommitVO seckillCommitVO = new SeckillCommitVO();
        BeanUtils.copyProperties(orderAddVO,seckillCommitVO);
        //千万要记得返回
        return seckillCommitVO;
    }
    //将秒杀订单信息转成普通订单信息
    private OrderAddDTO convertSeckillOrderToOrder(SeckillOrderAddDTO seckillOrderAddDTO) {
        OrderAddDTO orderAddDTO = new OrderAddDTO();
        //同名属性赋值
        BeanUtils.copyProperties(seckillOrderAddDTO,orderAddDTO);
        //seckillOrderAddDTO对象中包含了seckillOrderItemAddDTO
        //但是orderAddDTO中是List<OrderItemAddDTO>
        //声明List<OrderItemAddDTO>
        List<OrderItemAddDTO> orderItemAddDTOS = new ArrayList<>();
        OrderItemAddDTO orderItemAddDTO = new OrderItemAddDTO();
        BeanUtils.copyProperties(seckillOrderAddDTO.getSeckillOrderItemAddDTO(),orderItemAddDTO);
        orderItemAddDTOS.add(orderItemAddDTO);
        orderAddDTO.setOrderItems(orderItemAddDTOS);
        //返回orderAddDTO，这是普通订单的信息
        return orderAddDTO;
    }

    //获取当前登录用户信息
    public CsmallAuthenticationInfo getUserInfo(){
        //利用security上下文获取token
        UsernamePasswordAuthenticationToken token =
                (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
        //判断token是否为null
        if(token==null)
            throw new CoolSharkServiceException(ResponseCode.UNAUTHORIZED,"没有登录信息");
        //获取用户信息
        CsmallAuthenticationInfo userInfo = (CsmallAuthenticationInfo) token.getCredentials();
        return userInfo;
    }
    //获取当前登录用户的userId
    public Long getUserId(){
        return getUserInfo().getId();
    }
}
